//
// Created by denglibin on 19-10-23.
//

/**
 * 条件变量：
 * 条件变量不是锁，可以造成阻塞和唤醒阻塞，和锁配合使用
 * 使用条件变量实现生产者消费者（多生产者 多消费者）
 * */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
static int count = 10; // 退出条件
static int resource_num = 0;// 资源池资源数
static int max_num = 5; // 资源池最大值
static pthread_cond_t condition = PTHREAD_COND_INITIALIZER;//初始化条件变量
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 初始化互斥量

static int produce_num = 0; // 生产总数
static int consum_num = 0; // 消费总数
/**
 * 消费
 * @param arg
 * @return
 */
static void * consum(void * arg){
    while(1){

        pthread_mutex_lock(&mutex); // 获取锁
        while (resource_num == 0){ // 没资源了 考虑多个消费者 用while而不是if
            pthread_cond_wait(&condition, &mutex); // 阻塞等待 同时释放锁（如果是多个消费者，可能有多个消费者在此阻塞，被唤醒后会重新竞争锁）
        }
        resource_num--; // 消费一个资源
        consum_num ++;
        printf("consumer\n");
        // 释放锁 释放后可能时消费者竞争到锁也可能时生产者竞争到锁）
        pthread_mutex_unlock(&mutex);
       // pthread_cond_signal(&condition); //唤醒单个
        pthread_cond_broadcast(&condition); // 唤醒多个
    }
}
/**
 * 生产
 * @param arg
 * @return
 */
static void * product(void * arg){
    while(1){
        pthread_mutex_lock(&mutex);// 加锁
        while (resource_num == max_num){ //满了
            pthread_cond_wait(&condition, &mutex); // 阻塞等待 同时释放锁（如果有多个生产者，可能多个生产者都在次阻塞）多个被唤醒需要重新竞争锁
        }
        if(produce_num == count){
            break;
        }
        resource_num++;
        produce_num++;
        printf("producer\n");
        // 释放锁（释放后可能时消费者竞争到锁也可能时生产者竞争到锁）
        pthread_mutex_unlock(&mutex);
       // pthread_cond_signal(&condition);
       // 唤醒消费者
        pthread_cond_broadcast(&condition);

    }
    // 释放锁
    pthread_mutex_unlock(&mutex);
    // 唤醒
    // pthread_cond_signal(&condition); // 单个
    pthread_cond_broadcast(&condition); // 全部
}

static int main_(){
    pthread_t pro_t1; //生产者线程
    pthread_t pro_t2; //生产者线程
    pthread_t con_t1; //消费者线程
    pthread_t con_t2; //消费者线程

    pthread_create(&pro_t1, NULL, product, NULL);
    pthread_create(&pro_t2, NULL, product, NULL);
    pthread_create(&con_t1, NULL, consum, NULL);
    pthread_create(&con_t2, NULL, consum, NULL);

    pthread_join(pro_t1, NULL);
    pthread_join(pro_t2, NULL);
    pthread_join(con_t1, NULL);
    pthread_join(con_t2, NULL);

    pthread_mutex_destroy(&mutex);

    printf("consum num:%d\n", consum_num);
    printf("produce num:%d\n", produce_num);
    return 0;
}